clear environment

rm(list = ls())

load packages (install in you don’t have)

library(data.table)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
data.table 1.13.6 using 1 threads (see ?getDTthreads).  Latest news: r-datatable.com
**********
This installation of data.table has not detected OpenMP support. It should still work but in single-threaded mode.
This is a Mac. Please read https://mac.r-project.org/openmp/. Please engage with Apple and ask them for support. Check r-datatable.com for updates, and our Mac instructions here: https://github.com/Rdatatable/data.table/wiki/Installation. After several years of many reports of installation problems on Mac, it's time to gingerly point out that there have been no similar problems on Windows or Linux.
**********
library(colourpicker) #addin useful for selecting colours
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(cowplot) #theme for plotting
library(data.table) #package for manipulating and computing on data
library(raincloudplots) #https://wellcomeopenresearch.org/articles/4-63
Loading required package: ggplot2

packages that have data sets in them

library(Lahman) #has data sets related to baseball (AllstarFu ll and Pitching)
library(palmerpenguins) #has data sets related to penguins (penguins)

load in data

AllstarFull from Lahman package has baseball stats.

Pitching is another data set included with Lahman package and is more comprehensive.

palmerpenguins package has data sets related to penguins.

ggplot also has data set midwest included with it. Load this by doing data(“midwest”, package = “ggplot2”)

Need to copy and then convert the data to a data table

dat = copy(Pitching)
class(dat)
[1] "data.frame"
setDT(dat)
class(dat)
[1] "data.table" "data.frame"

examine data

notes on data

playerID: Player ID code

yearID: Year

stint: player’s stint (order of appearances within a season)

teamID: Team (factor)

lgID: League ID a factor with levels AA, AL, FL, NL, PL, UA

W: Wins

L: Losses

G: Games

GS: Games Started

CG: Complete Games

SHO: Shutouts

SV: Saves IPouts Outs Pitched (innings pitched x 3)

H: Hits

ER: Earned Runs

HR: Homeruns

BB: Walks

SO: Strikeouts

BAOpp: Opponent’s Batting Average

ERA: Earned Run Average

IBB: Intentional Walks

WP: Wild Pitches

HBP: Batters Hit By Pitch

BK: Balks

BFP: Batters faced by Pitcher

GF: Games Finished R Runs Allowed

SH: Sacrifices by opposing batters

SF: Sacrifice flies by opposing batters

GIDP: Grounded into double plays by opposing batter

Core elements of a ggplot plot:

(compiled from: https://ourcodingclub.github.io/tutorials/datavis/)

geom

Geometric object which defines the type of graph you are making.

It reads your data in the aesthetics mapping to know which variables to use, and creates the graph accordingly.

Some common types are:

aes

Short for aesthetics.

Usually placed within a geom_, this is where you specify your data source and variables, AND the properties of the graph which depend on those variables.

For instance, if you want all data points to be the same colour, you would define the ‘colour =’ argument outside the aes() function; if you want the data points to be coloured by a factor’s levels (e.g. by site or species), you specify the colour = argument inside the aes().

Some common things to include in aes are:

But note that different geoms have different aesthetics available (see cheatsheet below for example)

stat

a stat layer applies some statistical transformation to the underlying data: for instance, stat_smooth(method = ‘lm’) displays a linear regression line and confidence interval ribbon on top of a scatter plot (defined with geom_point()).

theme

A set of visual parameters that control the background, borders, grid lines, axes, text size, legend position, etc.

You can use pre-defined themes (e.g., theme_complot() from the cowplot package), create your own, or use a predefined theme and overwrite only the elements you don’t like.

Examples of elements within themes are:

e.g., axis.text.y = element_text(size = 12)

e.g., axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1)

[makes the x labels at an angle]

e.g., axis.title = element_text(size = 14, face = “plain”)

e.g., panel.grid = element_blank()

[Removes the background grid lines]

e.g., plot.margin = unit(c(1,1,1,1), units = , “cm”)

[Adds a 1cm margin around the plot]

e.g., legend.text = element_text(size = 12, face = “italic”)

[Setting the font for the legend text]

e.g., legend.title = element_blank()

[Remove the legend title - useful as sometimes this is excessive and the default is to include it]

e.g., legend.position = c(0.9, 0.9)))

+theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1),

axis.text.y = element_text(size = 12),

axis.title = element_text(size = 14, face = “plain”),

panel.grid = element_blank(),

plot.margin = unit(c(1,1,1,1), units = , “cm”),

legend.text = element_text(size = 12, face = “italic”),

legend.title = element_blank(),

legend.position = c(0.9, 0.9))

You define their properties with elements_…() functions. For example:

element_blank() would return something empty (ideal for removing background colour),

element_text(size = …, face = …, angle = …) lets you control all kinds of text properties.

# theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1), # making the years at a bit of an angle

#try a plot of home runs over year
ggplot(dat, aes(x=yearID, y=H))+geom_point()

#equivalent to
ggplot(dat)+geom_point(aes(x=yearID, y=H))

top tip: by encircling the ggplot in parenthesis () you get to assign a plot to a variable and plot it at the same time. useful if you want to save the plot or make it into a figure, refer to it later (e.g., replot, put in a panel with other figs) etc. Example here using the same plot as above

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)))

remove grey background with +theme_bw()

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_bw())

many other themes are available

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_classic())


(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_minimal())


(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_cowplot())

you can also create your own theme!

Just write it as a function. Example here taken from: https://rpubs.com/jenrichmond/W6LL

#library(data.table)
#library(palmerpenguins)
#library(cowplot)
##library(ggplot)

theme_jen <- function () {
  
  # define font up front
  font <- "Helvetica"  
  # this theme uses theme_bw as the base 
  
  theme_bw() %+replace%   
    theme(
      #get rid of grid lines/borders
      panel.border = element_blank(), 
      panel.grid.major = element_blank(), 
      panel.grid.minor = element_blank(), 
      # add white space top, right, bottom, left
      plot.margin = unit(c(1, 1, 1, 1), "cm"), 
      # custom axis title/text/lines
      axis.title = element_text(            
        family = font,                     
        size = 14),               
      axis.text = element_text(              
        family = font,                       
        size = 12),   
      # margin pulls text away from axis
      axis.text.x = element_text(           
        margin=margin(5, b = 10)),
      # black lines
      axis.line = element_line(colour = "black", size = rel(1)), 
      # custom plot titles, subtitles, captions
      plot.title = element_text(             
        family = font,              
        size = 18,
        hjust = -0.1,
        vjust = 4),
       # custom plot subtitles
      plot.subtitle = element_text(          
        family = font,                   
        size = 14, 
        hjust = 0,
        vjust = 3),
       # custom captions
      plot.caption = element_text(           
        family = font,                   
        size = 10,
        hjust = 1,
        vjust = 2), 
      # custom legend 
      legend.title = element_text(          
        family = font,           
        size = 10,                
        hjust = 0), 
      legend.text = element_text(          
        family = font,               
        size = 8,                     
        hjust = 0), 
      #no background on legend
      legend.key = element_blank(),   
      # white background on plot
      strip.background = element_rect(fill = "white",  
                                      colour = "black", 
                                      size = rel(2)), complete = TRUE)
  
}
#source("theme_jen.R") # the script/function containing custom ggplot theme
(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_jen())

add label to x and y axis plus add in various elements of theme

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + 
theme_classic()+
xlab('\nyear')+#\n adds blank line
ylab('n home runs')+ #\nadds blank line
theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1),  # making the years at a bit of an angle
axis.text.y = element_text(size = 12),
axis.title = element_text(size = 14, face = "plain"),                        
panel.grid = element_blank(),# Removing the background grid lines       
plot.margin = unit(c(1,1,1,1), units = , "cm"), # Adding a 1cm margin around the plot
legend.text = element_text(size = 12, face = "italic"), # Setting the font for the legend text
legend.title = element_blank(), # Removing the legend title
      legend.position = c(0.9, 0.9)))

might be claner to do the same plot on mean H per year

(plot1 = ggplot(dat[, .(H=mean(H)), by=yearID])+geom_point(aes(x=yearID, y=H)) + theme_classic()+
    xlab('\nyear')+            
    ylab('mean home runs per year')+          
    theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1),     
          axis.text.y = element_text(size = 12),
          axis.title = element_text(size = 14, face = "plain"),                        
          panel.grid = element_blank(),                                   
          plot.margin = unit(c(1,1,1,1), units = , "cm"),                 
          legend.text = element_text(size = 12, face = "italic"),         
          legend.title = element_blank(),                                 
          legend.position = c(0.9, 0.9)))

add a linear trendline using geom_smooth have to specficy method for this (method=“lm” or method=lm is fine). se is added by default (can add se=F to disable this)

(plot1 = ggplot(dat[, .(H=mean(H)), by=yearID], aes(x=yearID, y=H))+
    geom_point()+
    geom_smooth(method=lm)+
    theme_classic()+
    xlab('\nyear')+
    ylab('mean home runs per year')+
   theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1), axis.text.y = element_text(size = 12), axis.title = element_text(size = 14, face = "plain"), panel.grid = element_blank(),plot.margin = unit(c(1,1,1,1), units = , "cm"),             legend.text = element_text(size = 12, face = "italic"),       legend.title = element_blank(),legend.position = c(0.9, 0.9)))
`geom_smooth()` using formula 'y ~ x'

you can also add a specific formula in geom_smooth (e.g., y~x+x2+x3)

(plot1 = ggplot(dat[, .(H=mean(H)), by=yearID], aes(x=yearID, y=H))+
    geom_point()+
    geom_smooth(formula=y~x+x^2+x^3)+
    theme_classic()+
    xlab('\nyear')+
    ylab('mean home runs per year')+            
    theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1),
          axis.text.y = element_text(size = 12),
          axis.title = element_text(size = 14, face = "plain"),                        
          panel.grid = element_blank(),                                   
          plot.margin = unit(c(1,1,1,1), units = , "cm"),                 
          legend.text = element_text(size = 12, face = "italic"),         
          legend.title = element_blank(),                                 
          legend.position = c(0.9, 0.9)))
`geom_smooth()` using method = 'loess'

facet wrap this can be used to easily plot data in panels (e.g., plot mean home runs over time for each leagueID - here I also distinguish leagues by colour)/ seeing scales = “free_y” below means the y axis can vary from plot to plot. You can also use nrow = or ncol = to specify the numbers of rows/columns

dat$yearIDfact = as.factor(dat$yearID)

(plot1 = ggplot(dat[, .(H=mean(H)), by=.(lgID, yearID)], aes(x=yearID, y=H, colour=lgID))+
    geom_point()+
    facet_wrap(vars(lgID), scales = "free_y")+
    theme_classic()+
    xlab('\nyear')+            #\n adds blank line
    ylab('mean home runs per year'))

facet_grid does a similar thing but organised into columns of rows

here use rows based on teamID

(plot1 = ggplot(dat[, .(H=mean(H)), by=.(lgID, yearID)], aes(x=yearID, y=H, colour=lgID))+
    geom_point()+
    facet_grid(lgID ~ .)+
    theme_classic()+
    xlab('\nyear')+            #\n adds blank line
    ylab('mean home runs per year'))

columns based on teamID

(plot1 = ggplot(dat[, .(H=mean(H)), by=.(lgID, yearID)], aes(x=yearID, y=H, colour=lgID))+
    geom_point()+
    facet_grid(. ~ lgID)+
    theme_classic()+
    xlab('\nyear')+            #\n adds blank line
    ylab('mean home runs per year'))

bar plots

bar plots with error bars and individual data points

A special subcategory as this is the most common plot I end up having to do.

Note on data wrangling

box plots

exercises

ggplot cheatsheet

ggplot cheatsheet ggplot cheatsheet

LS0tCnRpdGxlOiAiZ2dwbG90X3RpY2tzdHJpY2tzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKIyMjIGNsZWFyIGVudmlyb25tZW50CgpgYGB7cn0Kcm0obGlzdCA9IGxzKCkpCmBgYAoKIyMjIGxvYWQgcGFja2FnZXMgKGluc3RhbGwgaW4geW91IGRvbid0IGhhdmUpCgpgYGB7cn0KbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGNvbG91cnBpY2tlcikgI2FkZGluIHVzZWZ1bCBmb3Igc2VsZWN0aW5nIGNvbG91cnMKbGlicmFyeShjb3dwbG90KSAjdGhlbWUgZm9yIHBsb3R0aW5nCmxpYnJhcnkoZGF0YS50YWJsZSkgI3BhY2thZ2UgZm9yIG1hbmlwdWxhdGluZyBhbmQgY29tcHV0aW5nIG9uIGRhdGEKbGlicmFyeShyYWluY2xvdWRwbG90cykgI2h0dHBzOi8vd2VsbGNvbWVvcGVucmVzZWFyY2gub3JnL2FydGljbGVzLzQtNjMKYGBgCgojIyMgcGFja2FnZXMgdGhhdCBoYXZlIGRhdGEgc2V0cyBpbiB0aGVtCgpgYGB7cn0KbGlicmFyeShMYWhtYW4pICNoYXMgZGF0YSBzZXRzIHJlbGF0ZWQgdG8gYmFzZWJhbGwgKEFsbHN0YXIgYW5kIFBpdGNoaW5nKQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKSAjaGFzIGRhdGEgc2V0cyByZWxhdGVkIHRvIHBlbmd1aW5zIChwZW5ndWlucykKCmBgYAoKIyMjIGxvYWQgaW4gZGF0YQoKQWxsc3RhckZ1bGwgZnJvbSBMYWhtYW4gcGFja2FnZSBoYXMgYmFzZWJhbGwgc3RhdHMuCgpQaXRjaGluZyBpcyBhbm90aGVyIGRhdGEgc2V0IGluY2x1ZGVkIHdpdGggTGFobWFuIHBhY2thZ2UgYW5kIGlzIG1vcmUKY29tcHJlaGVuc2l2ZS4KCnBhbG1lcnBlbmd1aW5zIHBhY2thZ2UgaGFzIGRhdGEgc2V0cyByZWxhdGVkIHRvIHBlbmd1aW5zLgoKZ2dwbG90IGFsc28gaGFzIGRhdGEgc2V0IG1pZHdlc3QgaW5jbHVkZWQgd2l0aCBpdC4gTG9hZCB0aGlzIGJ5IGRvaW5nCipkYXRhKCJtaWR3ZXN0IiwgcGFja2FnZSA9ICJnZ3Bsb3QyIikqCgpOZWVkIHRvIGNvcHkgYW5kIHRoZW4gY29udmVydCB0aGUgZGF0YSB0byBhIGRhdGEgdGFibGUKCmBgYHtyfQpkYXQgPSBjb3B5KFBpdGNoaW5nKQpjbGFzcyhkYXQpCnNldERUKGRhdCkKY2xhc3MoZGF0KQpgYGAKCmV4YW1pbmUgZGF0YQoKYGBge3J9CmhlYWQoZGF0KQpzdHIoZGF0KQpgYGAKCiMjIyAqKm5vdGVzIG9uIGRhdGEqKgoKKipwbGF5ZXJJRDoqKiBQbGF5ZXIgSUQgY29kZQoKKip5ZWFySUQ6KiogWWVhcgoKKipzdGludDoqKiBwbGF5ZXIncyBzdGludCAob3JkZXIgb2YgYXBwZWFyYW5jZXMgd2l0aGluIGEgc2Vhc29uKQoKKip0ZWFtSUQ6KiogVGVhbSAoZmFjdG9yKQoKKipsZ0lEOioqIExlYWd1ZSBJRCBhIGZhY3RvciB3aXRoIGxldmVscyBBQSwgQUwsIEZMLCBOTCwgUEwsIFVBCgoqKlc6KiogV2lucwoKKipMOioqIExvc3NlcwoKKipHOioqIEdhbWVzCgoqKkdTOioqIEdhbWVzIFN0YXJ0ZWQKCioqQ0c6KiogQ29tcGxldGUgR2FtZXMKCioqU0hPOioqIFNodXRvdXRzCgoqKlNWOioqIFNhdmVzIElQb3V0cyBPdXRzIFBpdGNoZWQgKGlubmluZ3MgcGl0Y2hlZCB4IDMpCgoqKkg6KiogSGl0cwoKKipFUjoqKiBFYXJuZWQgUnVucwoKKipIUjoqKiBIb21lcnVucwoKKipCQjoqKiBXYWxrcwoKKipTTzoqKiBTdHJpa2VvdXRzCgoqKkJBT3BwOioqIE9wcG9uZW50J3MgQmF0dGluZyBBdmVyYWdlCgoqKkVSQToqKiBFYXJuZWQgUnVuIEF2ZXJhZ2UKCioqSUJCOioqIEludGVudGlvbmFsIFdhbGtzCgoqKldQOioqIFdpbGQgUGl0Y2hlcwoKKipIQlA6KiogQmF0dGVycyBIaXQgQnkgUGl0Y2gKCioqQks6KiogQmFsa3MKCioqQkZQOioqIEJhdHRlcnMgZmFjZWQgYnkgUGl0Y2hlcgoKKipHRjoqKiBHYW1lcyBGaW5pc2hlZCBSIFJ1bnMgQWxsb3dlZAoKKipTSDoqKiBTYWNyaWZpY2VzIGJ5IG9wcG9zaW5nIGJhdHRlcnMKCioqU0Y6KiogU2FjcmlmaWNlIGZsaWVzIGJ5IG9wcG9zaW5nIGJhdHRlcnMKCioqR0lEUDoqKiBHcm91bmRlZCBpbnRvIGRvdWJsZSBwbGF5cyBieSBvcHBvc2luZyBiYXR0ZXIKCiMjIyAqKkNvcmUgZWxlbWVudHMgb2YgYSBnZ3Bsb3QgcGxvdDoqKgoKKGNvbXBpbGVkIGZyb206IDxodHRwczovL291cmNvZGluZ2NsdWIuZ2l0aHViLmlvL3R1dG9yaWFscy9kYXRhdmlzLz4pCgoqKmdlb20qKgoKR2VvbWV0cmljIG9iamVjdCB3aGljaCBkZWZpbmVzIHRoZSB0eXBlIG9mIGdyYXBoIHlvdSBhcmUgbWFraW5nLgoKSXQgcmVhZHMgeW91ciBkYXRhIGluIHRoZSBhZXN0aGV0aWNzIG1hcHBpbmcgdG8ga25vdyB3aGljaCB2YXJpYWJsZXMgdG8KdXNlLCBhbmQgY3JlYXRlcyB0aGUgZ3JhcGggYWNjb3JkaW5nbHkuCgpTb21lIGNvbW1vbiB0eXBlcyBhcmU6CgotICAgZ2VvbV9wb2ludCgpCgotICAgZ2VvbV9ib3hwbG90KCkKCi0gICBnZW9tX2hpc3RvZ3JhbSgpCgotICAgZ2VvbV9jb2woKQoKKiphZXMqKgoKU2hvcnQgZm9yIGFlc3RoZXRpY3MuCgpVc3VhbGx5IHBsYWNlZCB3aXRoaW4gYSBnZW9tXF8sIHRoaXMgaXMgd2hlcmUgeW91IHNwZWNpZnkgeW91ciBkYXRhCnNvdXJjZSBhbmQgdmFyaWFibGVzLCBBTkQgdGhlIHByb3BlcnRpZXMgb2YgdGhlIGdyYXBoIHdoaWNoIGRlcGVuZCBvbgp0aG9zZSB2YXJpYWJsZXMuCgpGb3IgaW5zdGFuY2UsIGlmIHlvdSB3YW50IGFsbCBkYXRhIHBvaW50cyB0byBiZSB0aGUgc2FtZSBjb2xvdXIsIHlvdQp3b3VsZCBkZWZpbmUgdGhlICdjb2xvdXIgPScgYXJndW1lbnQgKm91dHNpZGUqIHRoZSBhZXMoKSBmdW5jdGlvbjsgaWYKeW91IHdhbnQgdGhlIGRhdGEgcG9pbnRzIHRvIGJlIGNvbG91cmVkIGJ5IGEgZmFjdG9yJ3MgbGV2ZWxzIChlLmcuIGJ5CnNpdGUgb3Igc3BlY2llcyksIHlvdSBzcGVjaWZ5IHRoZSBjb2xvdXIgPSBhcmd1bWVudCAqaW5zaWRlKiB0aGUgYWVzKCkuCgpTb21lIGNvbW1vbiB0aGluZ3MgdG8gaW5jbHVkZSBpbiBhZXMgYXJlOgoKLSAgIHgKCi0gICB5CgotICAgZmlsbAoKLSAgIGNvbG91cgoKLSAgIHNpemUKCi0gICBzaGFwZQoKKipCdXQqKiBub3RlIHRoYXQgZGlmZmVyZW50IGdlb21zIGhhdmUgZGlmZmVyZW50IGFlc3RoZXRpY3MgYXZhaWxhYmxlCihzZWUgY2hlYXRzaGVldCBiZWxvdyBmb3IgZXhhbXBsZSkKCioqc3RhdCoqCgphIHN0YXQgbGF5ZXIgYXBwbGllcyBzb21lIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uIHRvIHRoZSB1bmRlcmx5aW5nCmRhdGE6IGZvciBpbnN0YW5jZSwgc3RhdF9zbW9vdGgobWV0aG9kID0gJ2xtJykgZGlzcGxheXMgYSBsaW5lYXIKcmVncmVzc2lvbiBsaW5lIGFuZCBjb25maWRlbmNlIGludGVydmFsIHJpYmJvbiBvbiB0b3Agb2YgYSBzY2F0dGVyIHBsb3QKKGRlZmluZWQgd2l0aCBnZW9tX3BvaW50KCkpLgoKKip0aGVtZSoqCgpBIHNldCBvZiB2aXN1YWwgcGFyYW1ldGVycyB0aGF0IGNvbnRyb2wgdGhlIGJhY2tncm91bmQsIGJvcmRlcnMsIGdyaWQKbGluZXMsIGF4ZXMsIHRleHQgc2l6ZSwgbGVnZW5kIHBvc2l0aW9uLCBldGMuCgpZb3UgY2FuIHVzZSBwcmUtZGVmaW5lZCB0aGVtZXMgKGUuZy4sIHRoZW1lX2NvbXBsb3QoKSBmcm9tIHRoZSBjb3dwbG90CnBhY2thZ2UpLCBjcmVhdGUgeW91ciBvd24sIG9yIHVzZSBhIHByZWRlZmluZWQgdGhlbWUgYW5kIG92ZXJ3cml0ZSBvbmx5CnRoZSBlbGVtZW50cyB5b3UgZG9uJ3QgbGlrZS4KCkV4YW1wbGVzIG9mIGVsZW1lbnRzIHdpdGhpbiB0aGVtZXMgYXJlOgoKLSAgICoqYXhpcy50ZXh0KioKCmUuZy4sIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikKCmUuZy4sIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdAo9IDEpCgpbbWFrZXMgdGhlIHggbGFiZWxzIGF0IGFuIGFuZ2xlXQoKLSAgICoqYXhpcy50aXRsZSoqCgplLmcuLCBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJwbGFpbiIpCgotICAgKipwYW5lbC5ncmlkKioKCmUuZy4sIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkKCltSZW1vdmVzIHRoZSBiYWNrZ3JvdW5kIGdyaWQgbGluZXNdCgotICAgKipwbG90Lm1hcmdpbioqCgplLmcuLCBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpCgpbQWRkcyBhIDFjbSBtYXJnaW4gYXJvdW5kIHRoZSBwbG90XQoKLSAgICoqbGVnZW5kIHRleHQqKgoKZS5nLiwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gIml0YWxpYyIpCgpbU2V0dGluZyB0aGUgZm9udCBmb3IgdGhlIGxlZ2VuZCB0ZXh0XQoKLSAgICoqbGVnZW5kLnRpdGxlKioKCmUuZy4sIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKQoKW1JlbW92ZSB0aGUgbGVnZW5kIHRpdGxlIC0gdXNlZnVsIGFzIHNvbWV0aW1lcyB0aGlzIGlzIGV4Y2Vzc2l2ZSBhbmQgdGhlCmRlZmF1bHQgaXMgdG8gaW5jbHVkZSBpdF0KCi0gICAqKmxlZ2VuZCBwb3NpdGlvbioqCgplLmcuLCBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCgotICAgKipwdXR0aW5nIGl0IGFsbCB0b2dldGhlci4uLioqCgordGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBhbmdsZSA9IDQ1LCB2anVzdCA9IDEsCmhqdXN0ID0gMSksCgpheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAoKYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAicGxhaW4iKSwKCnBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCgpwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLAoKbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gIml0YWxpYyIpLAoKbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAoKbGVnZW5kLnBvc2l0aW9uID0gYygwLjksIDAuOSkpCgpZb3UgZGVmaW5lIHRoZWlyIHByb3BlcnRpZXMgd2l0aCBlbGVtZW50c1xfLi4uKCkgZnVuY3Rpb25zLiBGb3IgZXhhbXBsZToKCmVsZW1lbnRfYmxhbmsoKSB3b3VsZCByZXR1cm4gc29tZXRoaW5nIGVtcHR5IChpZGVhbCBmb3IgcmVtb3ZpbmcKYmFja2dyb3VuZCBjb2xvdXIpLAoKZWxlbWVudF90ZXh0KHNpemUgPSAuLi4sIGZhY2UgPSAuLi4sIGFuZ2xlID0gLi4uKSBsZXRzIHlvdSBjb250cm9sIGFsbApraW5kcyBvZiB0ZXh0IHByb3BlcnRpZXMuCgpcIyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwKaGp1c3QgPSAxKSwgXCMgbWFraW5nIHRoZSB5ZWFycyBhdCBhIGJpdCBvZiBhbiBhbmdsZQoKYGBge3J9CiN0cnkgYSBwbG90IG9mIGhvbWUgcnVucyBvdmVyIHllYXIKZ2dwbG90KGRhdCwgYWVzKHg9eWVhcklELCB5PUgpKStnZW9tX3BvaW50KCkKI2VxdWl2YWxlbnQgdG8KZ2dwbG90KGRhdCkrZ2VvbV9wb2ludChhZXMoeD15ZWFySUQsIHk9SCkpCmBgYAoKdG9wIHRpcDogYnkgZW5jaXJjbGluZyB0aGUgZ2dwbG90IGluIHBhcmVudGhlc2lzICgpIHlvdSBnZXQgdG8gYXNzaWduIGEKcGxvdCB0byBhIHZhcmlhYmxlIGFuZCBwbG90IGl0IGF0IHRoZSBzYW1lIHRpbWUuIHVzZWZ1bCBpZiB5b3Ugd2FudCB0bwpzYXZlIHRoZSBwbG90IG9yIG1ha2UgaXQgaW50byBhIGZpZ3VyZSwgcmVmZXIgdG8gaXQgbGF0ZXIgKGUuZy4sIHJlcGxvdCwKcHV0IGluIGEgcGFuZWwgd2l0aCBvdGhlciBmaWdzKSBldGMuIEV4YW1wbGUgaGVyZSB1c2luZyB0aGUgc2FtZSBwbG90IGFzCmFib3ZlCgpgYGB7cn0KKHBsb3QxID0gZ2dwbG90KGRhdCkrZ2VvbV9wb2ludChhZXMoeD15ZWFySUQsIHk9SCkpKQpgYGAKCnJlbW92ZSBncmV5IGJhY2tncm91bmQgd2l0aCArdGhlbWVfYncoKQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2J3KCkpCmBgYAoKbWFueSBvdGhlciB0aGVtZXMgYXJlIGF2YWlsYWJsZQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2NsYXNzaWMoKSkKCihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX21pbmltYWwoKSkKCihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2Nvd3Bsb3QoKSkKYGBgCgp5b3UgY2FuIGFsc28gY3JlYXRlIHlvdXIgb3duIHRoZW1lIQoKSnVzdCB3cml0ZSBpdCBhcyBhIGZ1bmN0aW9uLiBFeGFtcGxlIGhlcmUgdGFrZW4gZnJvbToKPGh0dHBzOi8vcnB1YnMuY29tL2plbnJpY2htb25kL1c2TEw+CgpgYGB7cn0KI2xpYnJhcnkoZGF0YS50YWJsZSkKI2xpYnJhcnkocGFsbWVycGVuZ3VpbnMpCiNsaWJyYXJ5KGNvd3Bsb3QpCiMjbGlicmFyeShnZ3Bsb3QpCgp0aGVtZV9qZW4gPC0gZnVuY3Rpb24gKCkgewogIAogICMgZGVmaW5lIGZvbnQgdXAgZnJvbnQKICBmb250IDwtICJIZWx2ZXRpY2EiICAKICAjIHRoaXMgdGhlbWUgdXNlcyB0aGVtZV9idyBhcyB0aGUgYmFzZSAKICAKICB0aGVtZV9idygpICUrcmVwbGFjZSUgICAKICAgIHRoZW1lKAogICAgICAjZ2V0IHJpZCBvZiBncmlkIGxpbmVzL2JvcmRlcnMKICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAjIGFkZCB3aGl0ZSBzcGFjZSB0b3AsIHJpZ2h0LCBib3R0b20sIGxlZnQKICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMSwgMSwgMSwgMSksICJjbSIpLCAKICAgICAgIyBjdXN0b20gYXhpcyB0aXRsZS90ZXh0L2xpbmVzCiAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoICAgICAgICAgICAgCiAgICAgICAgZmFtaWx5ID0gZm9udCwgICAgICAgICAgICAgICAgICAgICAKICAgICAgICBzaXplID0gMTQpLCAgICAgICAgICAgICAgIAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoICAgICAgICAgICAgICAKICAgICAgICBmYW1pbHkgPSBmb250LCAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgc2l6ZSA9IDEyKSwgICAKICAgICAgIyBtYXJnaW4gcHVsbHMgdGV4dCBhd2F5IGZyb20gYXhpcwogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgIAogICAgICAgIG1hcmdpbj1tYXJnaW4oNSwgYiA9IDEwKSksCiAgICAgICMgYmxhY2sgbGluZXMKICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSByZWwoMSkpLCAKICAgICAgIyBjdXN0b20gcGxvdCB0aXRsZXMsIHN1YnRpdGxlcywgY2FwdGlvbnMKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgICAgCiAgICAgICAgZmFtaWx5ID0gZm9udCwgICAgICAgICAgICAgIAogICAgICAgIHNpemUgPSAxOCwKICAgICAgICBoanVzdCA9IC0wLjEsCiAgICAgICAgdmp1c3QgPSA0KSwKICAgICAgICMgY3VzdG9tIHBsb3Qgc3VidGl0bGVzCiAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoICAgICAgICAgIAogICAgICAgIGZhbWlseSA9IGZvbnQsICAgICAgICAgICAgICAgICAgIAogICAgICAgIHNpemUgPSAxNCwgCiAgICAgICAgaGp1c3QgPSAwLAogICAgICAgIHZqdXN0ID0gMyksCiAgICAgICAjIGN1c3RvbSBjYXB0aW9ucwogICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoICAgICAgICAgICAKICAgICAgICBmYW1pbHkgPSBmb250LCAgICAgICAgICAgICAgICAgICAKICAgICAgICBzaXplID0gMTAsCiAgICAgICAgaGp1c3QgPSAxLAogICAgICAgIHZqdXN0ID0gMiksIAogICAgICAjIGN1c3RvbSBsZWdlbmQgCiAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgCiAgICAgICAgZmFtaWx5ID0gZm9udCwgICAgICAgICAgIAogICAgICAgIHNpemUgPSAxMCwgICAgICAgICAgICAgICAgCiAgICAgICAgaGp1c3QgPSAwKSwgCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KCAgICAgICAgICAKICAgICAgICBmYW1pbHkgPSBmb250LCAgICAgICAgICAgICAgIAogICAgICAgIHNpemUgPSA4LCAgICAgICAgICAgICAgICAgICAgIAogICAgICAgIGhqdXN0ID0gMCksIAogICAgICAjbm8gYmFja2dyb3VuZCBvbiBsZWdlbmQKICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSwgICAKICAgICAgIyB3aGl0ZSBiYWNrZ3JvdW5kIG9uIHBsb3QKICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMikpLCBjb21wbGV0ZSA9IFRSVUUpCiAgCn0KYGBgCgpgYGB7cn0KI3NvdXJjZSgidGhlbWVfamVuLlIiKSAjIHRoZSBzY3JpcHQvZnVuY3Rpb24gY29udGFpbmluZyBjdXN0b20gZ2dwbG90IHRoZW1lCihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2plbigpKQpgYGAKCmFkZCBsYWJlbCB0byB4IGFuZCB5IGF4aXMgcGx1cyBhZGQgaW4gdmFyaW91cyBlbGVtZW50cyBvZiB0aGVtZQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIAp0aGVtZV9jbGFzc2ljKCkrCnhsYWIoJ1xueWVhcicpKyNcbiBhZGRzIGJsYW5rIGxpbmUKeWxhYignbiBob21lIHJ1bnMnKSsgI1xuYWRkcyBibGFuayBsaW5lCnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpLCAjIG1ha2luZyB0aGUgeWVhcnMgYXQgYSBiaXQgb2YgYW4gYW5nbGUKYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAicGxhaW4iKSwgICAgICAgICAgICAgICAgICAgICAgICAKcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwjIFJlbW92ZSB0aGUgYmFja2dyb3VuZCBncmlkIGxpbmVzICAgICAgIApwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLCAjIEFkZCBhIDFjbSBtYXJnaW4gYXJvdW5kIHRoZSBwbG90CmxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJpdGFsaWMiKSwgIyBTZXR0aW5nIHRoZSBmb250IGZvciB0aGUgbGVnZW5kIHRleHQKbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAjIFJlbW92aW5nIHRoZSBsZWdlbmQgdGl0bGUKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjksIDAuOSkpKQpgYGAKCm1pZ2h0IGJlIGNsYW5lciB0byBkbyB0aGUgc2FtZSBwbG90IG9uIG1lYW4gSCBwZXIgeWVhcgoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXRbLCAuKEg9bWVhbihIKSksIGJ5PXllYXJJRF0pK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2NsYXNzaWMoKSsKICAgIHhsYWIoJ1xueWVhcicpKyAgICAgICAgICAgIAogICAgeWxhYignbWVhbiBob21lIHJ1bnMgcGVyIHllYXInKSsgICAgICAgICAgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSwgICAgIAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gInBsYWluIiksICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLCAgICAgICAgICAgICAgICAgCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiaXRhbGljIiksICAgICAgICAgCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCmBgYAoKYWRkIGEgbGluZWFyIHRyZW5kbGluZSB1c2luZyBnZW9tX3Ntb290aCBoYXZlIHRvIHNwZWNmaWN5IG1ldGhvZCBmb3IKdGhpcyAobWV0aG9kPSJsbSIgb3IgbWV0aG9kPWxtIGlzIGZpbmUpLiBzZSBpcyBhZGRlZCBieSBkZWZhdWx0IChjYW4gYWRkCnNlPUYgdG8gZGlzYWJsZSB0aGlzKQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXRbLCAuKEg9bWVhbihIKSksIGJ5PXllYXJJRF0sIGFlcyh4PXllYXJJRCwgeT1IKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0pKwogICAgdGhlbWVfY2xhc3NpYygpKwogICAgeGxhYignXG55ZWFyJykrCiAgICB5bGFiKCdtZWFuIGhvbWUgcnVucyBwZXIgeWVhcicpKwogICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAicGxhaW4iKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSxwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLCAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiaXRhbGljIiksICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSxsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCmBgYAoKeW91IGNhbiBhbHNvIGFkZCBhIHNwZWNpZmljIGZvcm11bGEgaW4gZ2VvbV9zbW9vdGggKGUuZy4sIHlcfngreF4yK3heMykKCmBgYHtyfQoocGxvdDEgPSBnZ3Bsb3QoZGF0WywgLihIPW1lYW4oSCkpLCBieT15ZWFySURdLCBhZXMoeD15ZWFySUQsIHk9SCkpKwogICAgZ2VvbV9wb2ludCgpKwogICAgZ2VvbV9zbW9vdGgoZm9ybXVsYT15fngreF4yK3heMykrCiAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICB4bGFiKCdcbnllYXInKSsKICAgIHlsYWIoJ21lYW4gaG9tZSBydW5zIHBlciB5ZWFyJykrICAgICAgICAgICAgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJwbGFpbiIpLAogICAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsMSwxLDEpLCB1bml0cyA9ICwgImNtIiksCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCmBgYAoKZmFjZXQgd3JhcCB0aGlzIGNhbiBiZSB1c2VkIHRvIGVhc2lseSBwbG90IGRhdGEgaW4gcGFuZWxzIChlLmcuLCBwbG90Cm1lYW4gaG9tZSBydW5zIG92ZXIgdGltZSBmb3IgZWFjaCBsZWFndWVJRCAtIGhlcmUgSSBhbHNvIGRpc3Rpbmd1aXNoCmxlYWd1ZXMgYnkgY29sb3VyKS8gc2VlaW5nIHNjYWxlcyA9ICJmcmVlX3kiIGJlbG93IG1lYW5zIHRoZSB5IGF4aXMgY2FuCnZhcnkgZnJvbSBwbG90IHRvIHBsb3QuIFlvdSBjYW4gYWxzbyB1c2UgYG5yb3cgPWAgb3IgYG5jb2wgPWAgdG8gc3BlY2lmeQp0aGUgbnVtYmVycyBvZiByb3dzL2NvbHVtbnMKCmBgYHtyfQpkYXQkeWVhcklEZmFjdCA9IGFzLmZhY3RvcihkYXQkeWVhcklEKQoKKHBsb3QxID0gZ2dwbG90KGRhdFssIC4oSD1tZWFuKEgpKSwgYnk9LihsZ0lELCB5ZWFySUQpXSwgYWVzKHg9eWVhcklELCB5PUgsIGNvbG91cj1sZ0lEKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBmYWNldF93cmFwKHZhcnMobGdJRCksIHNjYWxlcyA9ICJmcmVlX3kiKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHhsYWIoJ1xueWVhcicpKyAgICAgICAgICAgICNcbiBhZGRzIGJsYW5rIGxpbmUKICAgIHlsYWIoJ21lYW4gaG9tZSBydW5zIHBlciB5ZWFyJykpCmBgYAoKZmFjZXRfZ3JpZCBkb2VzIGEgc2ltaWxhciB0aGluZyBidXQgb3JnYW5pc2VkIGludG8gY29sdW1ucyBvZiByb3dzCgpoZXJlIHVzZSByb3dzIGJhc2VkIG9uIHRlYW1JRAoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXRbLCAuKEg9bWVhbihIKSksIGJ5PS4obGdJRCwgeWVhcklEKV0sIGFlcyh4PXllYXJJRCwgeT1ILCBjb2xvdXI9bGdJRCkpKwogICAgZ2VvbV9wb2ludCgpKwogICAgZmFjZXRfZ3JpZChsZ0lEIH4gLikrCiAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICB4bGFiKCdcbnllYXInKSsgICAgICAgICAgICAjXG4gYWRkcyBibGFuayBsaW5lCiAgICB5bGFiKCdtZWFuIGhvbWUgcnVucyBwZXIgeWVhcicpKQpgYGAKCmNvbHVtbnMgYmFzZWQgb24gdGVhbUlECgpgYGB7cn0KKHBsb3QxID0gZ2dwbG90KGRhdFssIC4oSD1tZWFuKEgpKSwgYnk9LihsZ0lELCB5ZWFySUQpXSwgYWVzKHg9eWVhcklELCB5PUgsIGNvbG91cj1sZ0lEKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBmYWNldF9ncmlkKC4gfiBsZ0lEKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHhsYWIoJ1xueWVhcicpKyAgICAgICAgICAgICNcbiBhZGRzIGJsYW5rIGxpbmUKICAgIHlsYWIoJ21lYW4gaG9tZSBydW5zIHBlciB5ZWFyJykpCmBgYAoKIyMjICoqYmFyIHBsb3RzKioKCiMjIyAqKmJhciBwbG90cyB3aXRoIGVycm9yIGJhcnMgYW5kIGluZGl2aWR1YWwgZGF0YSBwb2ludHMqKgoKQSBzcGVjaWFsIHN1YmNhdGVnb3J5IGFzIHRoaXMgaXMgdGhlIG1vc3QgY29tbW9uIHBsb3QgSSBlbmQgdXAgaGF2aW5nIHRvCmRvLgoKKipOb3RlIG9uIGRhdGEgd3JhbmdsaW5nKioKCiMjIyAqKmJveCBwbG90cyoqCgojIyMgKipleGVyY2lzZXMqKgoKIyMjICoqUmVzb3VyY2VzL0xpbmtzKioKCk5vbiBleGhhdXN0aXZlIGxpc3Qgb2YgbGlua3MvcmVzb3VyY2VzIEkndmUgdXNlZCBpbiB0aGUgY291cnNlIG9mCmNvbXBpbGluZyB0aGlzIG5vdGVib29rCgo8aHR0cHM6Ly9ycHVicy5jb20vamVucmljaG1vbmQvVzZMTD4KCjxodHRwczovL3JhZmFsYWIuZ2l0aHViLmlvL2RzYm9vay9nZ3Bsb3QyLmh0bWw+Cgo8aHR0cDovL3Itc3RhdGlzdGljcy5jby9Db21wbGV0ZS1HZ3Bsb3QyLVR1dG9yaWFsLVBhcnQxLVdpdGgtUi1Db2RlLmh0bWw+Cgo8aHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvZGF0YXZpcy8+Cgo8aHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvZGF0YS12aXMtMi8+Cgo8aHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvcXVhbGl0YXRpdmUvPgoKIyMjICoqZ2dwbG90IGNoZWF0c2hlZXQqKgoKIVtnZ3Bsb3QgY2hlYXRzaGVldF0oaW1hZ2VzL2dncGxvdDItY2hlYXRzaGVldGEucG5nKSAhW2dncGxvdApjaGVhdHNoZWV0XShpbWFnZXMvZ2dwbG90Mi1jaGVhdHNoZWV0Yi5wbmcpCg==